////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "SmegEditorCanvas.hpp"

#include <sstream>

#include <SmegTools/Debug/ProfilerManager.hpp>
#include <SmegLight/Light/LightSystem.hpp>

#include <SmegCore/Entity/EntityManager.hpp>
#include <SmegCore/Entity/BaseEntity.hpp>

#include "../ObjectHandler/ObjectHandlerFactory.hpp"

#include "../Form/SmegEditorFrame.h"
#include "SmegPlaceable.hpp"

////////////////////////////////////////////////////////////
/// Construct the SmegEditorCanvas
////////////////////////////////////////////////////////////
SmegEditorCanvas::SmegEditorCanvas(wxWindow*        Parent,
                                    wxWindowID      Id,
                                    const wxPoint&  Position, 
                                    const wxSize&   Size, 
                                    long            Style) :
    wxSFMLCanvas(Parent, Id, Position, Size, Style),
    m_bUpdateEntities( false ),
    m_bShowQuadTree( false ),
    m_bShowProfiler( false ),
    m_pWidgetDragged( NULL ),
    m_pPlaceableDragged( NULL ),
    m_PlaceableWidgets(),
    m_SelectedObjects(),
    m_isControlDown( false ),
    m_infoView( sf::Vector2f(0, 0), sf::Vector2f(1, 1) ),
    m_clock()
{
    // EntityManager
    m_EntityManager = smeg::core::EntityManager::GetInstance();
    m_EntityManager->Init( this );

    // LightSystem
    m_LightSystem = smeg::light::LightSystem::GetInstance();
    m_LightSystem->Init( smeg::math::AABB( sf::Vector2f( 0.0f, 0.0f ), sf::Vector2f( 1280.0f, 1024.0f )), this );
    m_LightSystem->SetAmbientColor( sf::Color( 10,10,10 ) );

    // Profiler
    PROFILER_INIT( this )
    PROFILER_ADD( "ComputeLights" )
    PROFILER_ADD( "RenderFinalTexture" )
    PROFILER_ADD( "RenderLights" )
    PROFILER_ADD( "RenderEntities" )

    // Connect Events
    this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(SmegEditorCanvas::OnKeyDown));
    this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(SmegEditorCanvas::OnKeyUp));
    this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(SmegEditorCanvas::OnMouseLeftDown));
    this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(SmegEditorCanvas::OnMouseLeftUp));
    this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(SmegEditorCanvas::OnMouseEnterWindow));
    this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(SmegEditorCanvas::OnMouseLeaveWindow));
    this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(SmegEditorCanvas::OnMouseMiddleDown));
    this->Connect(wxEVT_MOTION, wxMouseEventHandler(SmegEditorCanvas::OnMouseMove));
    this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(SmegEditorCanvas::OnMouseWheel));

    // DEBUG Info
    if ( m_font.loadFromFile( "data/fonts/COUR.ttf" ) ) {
        m_info.setFont( m_font );
        m_info.setCharacterSize( 12 );
        m_info.setColor( sf::Color::White );
    }
}
    
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
SmegEditorCanvas::~SmegEditorCanvas() {
}

////////////////////////////////////////////////////////////
/// Set the selected object
////////////////////////////////////////////////////////////
void SmegEditorCanvas::SetSelectedObject( smeg::core::PlaceableObject* _pSelected ) {
    ResetSelectedObjects();

    m_SelectedObjects.resize( 1 );
    m_SelectedObjects[0] = _pSelected;
#ifdef _DEBUG
    m_SelectedObjects[0]->DEBUG_DebugRender = true;
#endif

    // Widget
    m_PlaceableWidgets.resize( 1 );
    m_PlaceableWidgets[0].setPosition( m_SelectedObjects[0]->GetCenter() );
}

////////////////////////////////////////////////////////////
/// Set the selected objects
////////////////////////////////////////////////////////////
void SmegEditorCanvas::SetSelectedObjects( const std::vector< smeg::core::PlaceableObject* >& _Selected ) {
    ResetSelectedObjects();

    m_SelectedObjects.assign( _Selected.begin(), _Selected.end() );

    // Widget
    m_PlaceableWidgets.resize( _Selected.size() );
    for ( unsigned i = 0; i < _Selected.size(); ++i ) {
#ifdef _DEBUG
        m_SelectedObjects[i]->DEBUG_DebugRender = true;
#endif
        m_PlaceableWidgets[i].setPosition( m_SelectedObjects[i]->GetCenter() );
    }
}

////////////////////////////////////////////////////////////
/// Get the selected objects
////////////////////////////////////////////////////////////
const std::vector< smeg::core::PlaceableObject* >& SmegEditorCanvas::GetSelectedObjects() const {
    return m_SelectedObjects;
}

////////////////////////////////////////////////////////////
/// The selected object was moved
////////////////////////////////////////////////////////////
void SmegEditorCanvas::OnObjectMove( const sf::Vector2f& _Move ) {
    for ( unsigned i = 0; i < m_PlaceableWidgets.size(); ++i ) {
        m_SelectedObjects[i]->Move( _Move );
        m_PlaceableWidgets[i].move( _Move );
    }
}

////////////////////////////////////////////////////////////
/// The selected object was rotated
////////////////////////////////////////////////////////////
void SmegEditorCanvas::OnObjectRotate( float _rot ) {
    float widgetRot = 0.0f;
    SmegPlaceable::eModeEnum mode = m_PlaceableWidgets[0].GetMode();
    if ( SmegPlaceable::eMode_Rotation == mode ) {
        widgetRot = _rot;
    }

    for ( unsigned i = 0; i < m_PlaceableWidgets.size(); ++i ) {
        m_SelectedObjects[i]->RotateDeg( _rot );
        m_PlaceableWidgets[i].rotate( widgetRot );
    }
}

////////////////////////////////////////////////////////////
/// Reset the selected object
////////////////////////////////////////////////////////////
void SmegEditorCanvas::ResetSelectedObjects() {
#ifdef _DEBUG
    for ( unsigned i = 0; i < m_SelectedObjects.size(); ++i ) {
        m_SelectedObjects[i]->DEBUG_DebugRender = false;
    }
#endif

    m_SelectedObjects.clear();
    m_PlaceableWidgets.clear();
    
    m_pPlaceableDragged = NULL;
    m_pWidgetDragged = NULL;
}

////////////////////////////////////////////////////////////
/// Notification for the derived class that moment is good
/// for doing its update and drawing stuff
////////////////////////////////////////////////////////////
void SmegEditorCanvas::OnUpdate() {
float fDeltaTime = m_clock.getElapsedTime().asSeconds();
    m_clock.restart();

    // On efface la vue
    clear( sf::Color::White );
    
    if ( m_bUpdateEntities ) {
        m_EntityManager->UpdateEntities( fDeltaTime );
    }
    m_EntityManager->RenderEntities();

    m_LightSystem->ComputeLights();
    m_LightSystem->RenderLights();
    
#ifdef _DEBUG
    m_EntityManager->DebugRender();
    m_LightSystem->DebugRender( m_bShowQuadTree );
#endif

    static_cast<SmegEditorFrame*>(GetParent())->UpdateAndRenderHandlers();

    for ( unsigned i = 0; i < m_SelectedObjects.size(); ++i ) {
        if ( SmegPlaceable::eMode_Rotation == m_PlaceableWidgets[i].GetMode() ) {
            m_PlaceableWidgets[i].setRotation( m_SelectedObjects[i]->GetAngleDeg() );
        } else {
            m_PlaceableWidgets[i].setRotation( 0.0f );
        }
        draw(m_PlaceableWidgets[i]);
    }
    
    // DEBUG Info
    PROFILER_DEBUG_RENDER( m_bShowProfiler, false )

    // Set the text to the top left
    setView( m_infoView );
    
    std::ostringstream stream;
    stream << "FPS : " << (int)(1.0f / fDeltaTime) << std::endl << std::endl;
    stream << "Lights : " << smeg::light::LightSystem::GetInstance()->GetNumLights() << std::endl;
    stream << "Hull : " << smeg::light::LightSystem::GetInstance()->GetNumHulls() << std::endl;
    m_info.setString( stream.str() );
    
    draw( m_info );

    setView( m_currentView );
}

////////////////////////////////////////////////////////////
void SmegEditorCanvas::UpdateParentWindow() {
    if ( m_pWidgetDragged ) {
        SmegEditorFrame* parent = static_cast<SmegEditorFrame*>(GetParent());

        if ( 1 == m_PlaceableWidgets.size() ) {
            SmegPlaceable::eModeEnum mode = m_pWidgetDragged->GetMode();
            // Only one object is selected, update its properties
            if ( SmegPlaceable::eMode_Translation == mode ) {
                parent->OnObjectChangePosition( m_pPlaceableDragged->GetCenter() );
            } else if ( SmegPlaceable::eMode_Rotation == mode ) {
                parent->OnObjectChangeRotation( m_pPlaceableDragged->GetAngleDeg() );
            }
        }
        
        parent->SetProjectModified( true );
    }
}

void SmegEditorCanvas::EndDrag() {
    if ( m_pWidgetDragged ) {
        m_pWidgetDragged->StopDragSelection();
        m_pPlaceableDragged = NULL;
        m_pWidgetDragged = NULL;
    }
}

////////////////////////////////////////////////////////////
/// Events
////////////////////////////////////////////////////////////
void SmegEditorCanvas::OnKeyDown( wxKeyEvent& event ) {
    if ( WXK_CONTROL == event.GetKeyCode() ) {
        m_isControlDown = true;
    }

    // Placeable Widget Mode
    if ( m_isControlDown && L'K' == event.m_uniChar ) {
        for ( unsigned i = 0; i < m_PlaceableWidgets.size(); ++i ) {
            m_PlaceableWidgets[i].SetMode( SmegPlaceable::eMode_Translation );
        }
    }

    if ( m_isControlDown && L'L' == event.m_uniChar ) {
        for ( unsigned i = 0; i < m_PlaceableWidgets.size(); ++i ) {
            m_PlaceableWidgets[i].SetMode( SmegPlaceable::eMode_Rotation );
        }
    }

    if ( m_isControlDown && L'M' == event.m_uniChar ) {
        for ( unsigned i = 0; i < m_PlaceableWidgets.size(); ++i ) {
            m_PlaceableWidgets[i].SetMode( SmegPlaceable::eMode_None );
        }
    }

    event.Skip();
}

void SmegEditorCanvas::OnKeyUp( wxKeyEvent& event ) {
    if ( WXK_CONTROL == event.GetKeyCode() ) {
        m_isControlDown = false;
    }

    event.Skip();
}

void SmegEditorCanvas::OnMouseLeftDown( wxMouseEvent& event ) {
    this->SetFocus();

    sf::Vector2f mousePosView( GetMousePosInViewSpace( event.GetPosition() ) );
    for (unsigned i = 0; i < m_PlaceableWidgets.size(); ++i) {
        if ( SmegPlaceable::eDrag_None != m_PlaceableWidgets[i].BeginDragSelection( mousePosView ) ){
            m_pPlaceableDragged = m_SelectedObjects[i];
            m_pWidgetDragged = &m_PlaceableWidgets[i];
            return;
        }
    }

    event.Skip();
}

void SmegEditorCanvas::OnMouseLeftUp( wxMouseEvent& event ) {
    UpdateParentWindow();
    EndDrag();

    event.Skip();
}

void SmegEditorCanvas::OnMouseEnterWindow( wxMouseEvent& event ) {
    if ( !event.LeftIsDown() ) {
        EndDrag();
    }

    event.Skip();
}

void SmegEditorCanvas::OnMouseLeaveWindow( wxMouseEvent& event ) {
    UpdateParentWindow();

    event.Skip();
}

void SmegEditorCanvas::OnMouseMiddleDown( wxMouseEvent& event ) {
    m_LastMousePos = event.GetPosition();

    event.Skip();
}

void SmegEditorCanvas::OnMouseMove( wxMouseEvent& event ) {
    if ( event.MiddleIsDown() ) {
        wxPoint newMousePos = event.GetPosition(),
            mouseMotion = m_LastMousePos - newMousePos;

        m_currentView.move( mouseMotion.x, mouseMotion.y );
        m_LightSystem->SetView(m_currentView);
        m_LastMousePos = newMousePos;
    }

    if ( m_SelectedObjects.size() > 0 ) {
        sf::Vector2f mousePosView( GetMousePosInViewSpace( event.GetPosition() ) );
        if ( event.LeftIsDown() && m_pPlaceableDragged ) {
            // Reset the unselected widgets
            for (unsigned i = 0; i < m_PlaceableWidgets.size(); ++i) {
                m_PlaceableWidgets[i].ResetSelection();
            }

            // Drag the selected one
            m_pWidgetDragged->DragSelection( mousePosView );
            SmegPlaceable::eModeEnum mode = m_pWidgetDragged->GetMode();
            
            if ( 1 == m_PlaceableWidgets.size() ) {
                // Only one object is selected, update its properties
                if ( SmegPlaceable::eMode_Translation == mode ) {
                    m_pPlaceableDragged->SetCenter( m_pWidgetDragged->getPosition() );
                } else if ( SmegPlaceable::eMode_Rotation == mode ) {
                    m_pPlaceableDragged->SetAngleDeg( m_pWidgetDragged->getRotation() );
                }
            } else {
                sf::Vector2f moveVec = m_pWidgetDragged->getPosition() - m_pPlaceableDragged->GetCenter();
                float rotAngle = m_pWidgetDragged->getRotation() - m_pPlaceableDragged->GetAngleDeg();
                // Multiple objects selected move/rotate them as well as their associated widgets
                for (unsigned i = 0; i < m_SelectedObjects.size(); ++i) {
                    if ( SmegPlaceable::eMode_Translation == mode ) {
                        m_SelectedObjects[i]->Move( moveVec );
                        if ( m_pWidgetDragged != &m_PlaceableWidgets[i] ) {
                            m_PlaceableWidgets[i].move( moveVec );
                        }
                    } else if ( SmegPlaceable::eMode_Rotation == mode ) {
                        m_SelectedObjects[i]->RotateDeg( rotAngle );
                        if ( m_pWidgetDragged != &m_PlaceableWidgets[i] ) {
                            m_PlaceableWidgets[i].rotate( rotAngle );
                        }
                    }
                }
            }
        } else {
            SmegPlaceable::eDragEnum dragMode = SmegPlaceable::eDrag_None;
            for (unsigned i = 0; i < m_PlaceableWidgets.size(); ++i) {
                if ( SmegPlaceable::eDrag_None == dragMode ) {
                    dragMode = m_PlaceableWidgets[i].SetSelection( mousePosView );
                } else {
                    m_PlaceableWidgets[i].ResetSelection();
                }
            }
        }
    }

    event.Skip();
}

void SmegEditorCanvas::OnMouseWheel( wxMouseEvent& event ) {
    wxMouseWheelAxis axis = event.GetWheelAxis();
    int rotation = event.GetWheelRotation();
    int delta = event.GetWheelDelta();

    if ( rotation > 0 ) {
        // Zoom in
        m_currentView.zoom( 0.5f );
    } else if ( rotation < 0 ) {
        // Zoom out
        m_currentView.zoom( 2.0f );
    }
    m_LightSystem->SetView(m_currentView);

    event.Skip();
}

void SmegEditorCanvas::OnSizeEnd( wxTimerEvent& event ) {
    wxSFMLCanvas::OnSizeEnd(event);

    m_infoView.setSize( getSize().x, getSize().y );
    m_info.setPosition( -m_infoView.getSize() * 0.5f );
    m_LightSystem->SetView(m_currentView);

    event.Skip();
}